package com.hmdp.service.impl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.User;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWoker;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@RequiredArgsConstructor
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    private final RedisIdWoker redisIdWoker;
    private final ISeckillVoucherService seckillVoucherService;
    private final StringRedisTemplate stringRedisTemplate;
    private final RedissonClient redissonClient;

    @Override
    public Result seckillVoucherById(Long voucherId) {
        // 查询优惠券信息
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        // 判断秒杀是否开始
        LocalDateTime beginTime = voucher.getBeginTime();
        LocalDateTime now = LocalDateTime.now();
        if(now.isBefore(beginTime)) {
            return Result.fail("活动尚未开始！");
        }
        // 秒杀未开始，返回异常结果
        LocalDateTime endTime = voucher.getEndTime();
        LocalDateTime nowTime = LocalDateTime.now();
        if(nowTime.isAfter(endTime)) {
            return Result.fail("活动已结束！");
        }
        // 库存不充足，返回异常结果
        Integer stock = voucher.getStock();
        if(stock < 1) {
            return Result.fail("库存不足！");
        }

        Long UserId = UserHolder.getUser().getId();

        // 创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order:" + UserId, stringRedisTemplate);
        RLock lock = redissonClient.getLock("order:" + UserId);

        boolean islock = false;
        try {
            islock = lock.tryLock(1,10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (!islock){
            return Result.fail("一人只能下一单！");
        }
        try {
            // 获取代理对象
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.getResult(voucherId); // 事务失效：this调用
        } finally {
            lock.unlock();
        }

    }

    @Transactional
    public Result getResult(Long voucherId) {
        // 一人一单
        Long UserId = UserHolder.getUser().getId();
        int count = query().eq("user_id", UserId).eq("voucher_id", voucherId).count();
        if(count > 0) {
            return Result.fail("用户已经购买过了！");
        }
        // 在秒杀时间段内，库存充足，扣减库存(乐观锁) cas法解决超卖问题 先比较再设置，设置数据时需要与刚开始查询到的数据比较，即sql语句需要多加一个条件
        boolean success =
        seckillVoucherService.update().setSql("stock = stock-1")  // set stock = stock -1
                .eq("voucher_id", voucherId)  // where id = id
                .gt("stock",0).update(); // and stock >0
        if(!success) {
            return Result.fail("库存不足！");
        }
        // 创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        // 1. 获取全局唯一id
        long orderID = redisIdWoker.nextID("order");
        voucherOrder.setId(orderID);
        // 2.  获取用户id
        UserId = UserHolder.getUser().getId();
        voucherOrder.setUserId(UserId);
        // 3. 获取代金券id
        voucherOrder.setVoucherId(voucherId);
        this.save(voucherOrder);
        return Result.ok(orderID);
    }
}
